package plugin

import (
	"gorm.io/gorm"
	"reflect"
	"strings"
)

type ProcessType string // 操作类型

var (
	ProcessBefore ProcessType = "before"
	ProcessAfter  ProcessType = "after"

	CodecTypeAES    = "aes"
	CodecTypeBase64 = "base64"
)

type codecPlugin struct{}

type Option func(p *codecPlugin)

// NewCodecPlugin create a new codec plugin
func NewCodecPlugin(opts ...Option) gorm.Plugin {
	p := &codecPlugin{}
	for _, opt := range opts {
		opt(p)
	}
	return p
}

// Name return plugin name
func (p codecPlugin) Name() string {
	return "codec_plugin"
}

// Initialize initialize plugin
func (p codecPlugin) Initialize(db *gorm.DB) error {
	// 1. 获取db的Callback对象
	cb := db.Callback()

	// 2. 将需要执行的操作以及对应的回调函数注册到Callback对象
	hooks := []struct {
		name   string
		action error
	}{
		// 创建操作前的回调函数
		{name: "before:create", action: cb.Create().Before("gorm:create").Register("codec:before:create", HandleCallback("before:create", ProcessBefore))},
		// 创建操作后的回调函数
		{name: "after:create", action: cb.Create().After("gorm:create").Register("codec:after:create", HandleCallback("after:create", ProcessAfter))},
		// 更新操作前的回调函数
		{name: "before:update", action: cb.Update().Before("gorm:update").Register("codec:before:update", HandleCallback("before:update", ProcessBefore))},
		// 更新操作后的回调函数
		{name: "after:update", action: cb.Update().After("gorm:update").Register("codec:after:update", HandleCallback("after:update", ProcessAfter))},
		// 查询操作后的回调函数
		{name: "after:query", action: cb.Query().After("gorm:query").Register("codec:after:query", HandleCallback("after:query", ProcessAfter))},
	}

	for _, h := range hooks {
		err := h.action
		if err != nil {
			return err
		}
	}

	return nil
}

// HandleCallback 处理回调函数
func HandleCallback(name string, t ProcessType) func(db *gorm.DB) {
	return func(db *gorm.DB) {
		// 如果 db.Error 为 nil，说明没有错误，可以继续执行
		if db.Error != nil || db.Statement.Schema == nil {
			return
		}

		handle := func(rVal reflect.Value, fn func(kind string, plainText string) (string, error)) error {
			for _, field := range db.Statement.Schema.Fields {
				if field.FieldType.Kind() != reflect.String {
					continue
				}

				value := field.ReflectValueOf(db.Statement.Context, rVal)
				str := value.String()
				if str == "" {
					continue
				}

				tag := field.Tag.Get("codec")
				if len(tag) == 0 {
					continue
				}
				str, err := fn(tag, str)
				if err != nil {
					_ = db.AddError(err)
					return err
				}
				_ = field.Set(db.Statement.Context, rVal, str)
			}

			return nil
		}

		switch db.Statement.ReflectValue.Kind() {
		case reflect.Slice, reflect.Array:
			rValLen := db.Statement.ReflectValue.Len()
			for i := 0; i < rValLen; i++ {
				rVal := db.Statement.ReflectValue.Index(i)
				if rVal.Kind() == reflect.Ptr {
					rVal = rVal.Elem()
				}

				if t == ProcessBefore {
					err := handle(rVal, handleEncrpyt)
					if err != nil {
						return
					}
				} else if t == ProcessAfter {
					err := handle(rVal, handleDecrypt)
					if err != nil {
						return
					}
				}

			}
		case reflect.Struct:
			rVal := db.Statement.ReflectValue
			if t == ProcessBefore {
				err := handle(rVal, handleEncrpyt)
				if err != nil {
					return
				}
			} else if t == ProcessAfter {
				err := handle(rVal, handleDecrypt)
				if err != nil {
					return
				}
			}
		}
	}
}

// handleEncrpyt 处理加密. kind: 加密类型, plainText: 明文
func handleEncrpyt(kind string, plainText string) (string, error) {
	switch strings.ToLower(kind) {
	case CodecTypeAES:
		//return AesEncryptDefault(plainText)
	case CodecTypeBase64:
		//return Base64StdEncode(plainText), nil
	default:
		return plainText, nil
	}
	return plainText, nil

}

// handleDecrypt 处理解密. kind: 加密类型, plainText: 明文
func handleDecrypt(kind string, plainText string) (string, error) {
	switch strings.ToLower(kind) {
	case CodecTypeAES:
		//return AesDecryptDefault(plainText)
	case CodecTypeBase64:
		//return Base64StdDecode(plainText)
	default:
		return plainText, nil
	}

	return plainText, nil
}
